Passed
Branch master (0c09a6)
by Stephan
01:45
created

scrollspy.js ➔ ?!?   C

Complexity

Conditions 5
Paths 16

Size

Total Lines 365
Code Lines 211

Duplication

Lines 38
Ratio 10.41 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 5
eloc 211
nc 16
nop 2
dl 38
loc 365
rs 6.5333
c 1
b 0
f 0

19 Functions

Rating   Name   Duplication   Size   Complexity  
A scrollspy.js ➔ ... ➔ _createClass 0 5 3
A scrollspy.js ➔ ... ➔ $(window).on 0 10 2
A scrollspy.js ➔ ... ➔ _getOffsetHeight 0 3 2
A scrollspy.js ➔ ... ➔ _defineProperty 0 14 2
A scrollspy.js ➔ ... ➔ _defineProperties 0 9 3
B scrollspy.js ➔ ... ➔ ScrollSpy.constructor 20 220 1
A scrollspy.js ➔ ... ➔ ScrollSpy 0 18 2
A scrollspy.js ➔ ... ➔ dispose 0 12 1
A scrollspy.js ➔ ... ➔ get 0 3 1
A scrollspy.js ➔ ... ➔ _clear 0 7 1
C scrollspy.js ➔ ... ➔ _process 0 39 9
A scrollspy.js ➔ ... ➔ refresh 0 38 4
A scrollspy.js ➔ ... ➔ _getConfig 0 17 5
A scrollspy.js ➔ ... ➔ _jQueryInterface 20 20 1
A scrollspy.js ➔ ... ➔ _activate 0 28 2
A scrollspy.js ➔ ... ➔ _getScrollHeight 0 3 1
A scrollspy.js ➔ ... ➔ _getScrollTop 0 3 2
A scrollspy.js ➔ ... ➔ $.fn[NAME].noConflict 0 4 1
A scrollspy.js ➔ ... ➔ _objectSpread 18 18 4

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
/*!
2
  * Bootstrap scrollspy.js v4.3.1 (https://getbootstrap.com/)
3
  * Copyright 2011-2019 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
4
  * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
5
  */
6
(function (global, factory) {
7
  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('jquery'), require('./util.js')) :
8
  typeof define === 'function' && define.amd ? define(['jquery', './util.js'], factory) :
0 ignored issues
show
Bug introduced by
The variable define seems to be never declared. If this is a global, consider adding a /** global: define */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
9
  (global = global || self, global.ScrollSpy = factory(global.jQuery, global.Util));
0 ignored issues
show
Best Practice introduced by
If you intend to check if the variable self is declared in the current environment, consider using typeof self === "undefined" instead. This is safe if the variable is not actually declared.
Loading history...
Comprehensibility introduced by
Usage of the sequence operator is discouraged, since it may lead to obfuscated code.

The sequence or comma operator allows the inclusion of multiple expressions where only is permitted. The result of the sequence is the value of the last expression.

This operator is most often used in for statements.

Used in another places it can make code hard to read, especially when people do not realize it even exists as a seperate operator.

This check looks for usage of the sequence operator in locations where it is not necessary and could be replaced by a series of expressions or statements.

var a,b,c;

a = 1, b = 1,  c= 3;

could just as well be written as:

var a,b,c;

a = 1;
b = 1;
c = 3;

To learn more about the sequence operator, please refer to the MDN.

Loading history...
10
}(this, function ($, Util) { 'use strict';
11
12
  $ = $ && $.hasOwnProperty('default') ? $['default'] : $;
13
  Util = Util && Util.hasOwnProperty('default') ? Util['default'] : Util;
14
15
  function _defineProperties(target, props) {
16
    for (var i = 0; i < props.length; i++) {
17
      var descriptor = props[i];
18
      descriptor.enumerable = descriptor.enumerable || false;
19
      descriptor.configurable = true;
20
      if ("value" in descriptor) descriptor.writable = true;
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
21
      Object.defineProperty(target, descriptor.key, descriptor);
22
    }
23
  }
24
25
  function _createClass(Constructor, protoProps, staticProps) {
26
    if (protoProps) _defineProperties(Constructor.prototype, protoProps);
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
27
    if (staticProps) _defineProperties(Constructor, staticProps);
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
28
    return Constructor;
29
  }
30
31
  function _defineProperty(obj, key, value) {
32
    if (key in obj) {
33
      Object.defineProperty(obj, key, {
34
        value: value,
35
        enumerable: true,
36
        configurable: true,
37
        writable: true
38
      });
39
    } else {
40
      obj[key] = value;
41
    }
42
43
    return obj;
44
  }
45
46 View Code Duplication
  function _objectSpread(target) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
47
    for (var i = 1; i < arguments.length; i++) {
48
      var source = arguments[i] != null ? arguments[i] : {};
0 ignored issues
show
Best Practice introduced by
Comparing arguments.i to null using the != operator is not safe. Consider using !== instead.
Loading history...
49
      var ownKeys = Object.keys(source);
50
51
      if (typeof Object.getOwnPropertySymbols === 'function') {
52
        ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) {
53
          return Object.getOwnPropertyDescriptor(source, sym).enumerable;
0 ignored issues
show
Bug introduced by
The variable source is changed as part of the for loop for example by arguments.i != null ? arguments.i: {} on line 48. Only the value of the last iteration will be visible in this function if it is called after the loop.
Loading history...
54
        }));
55
      }
56
57
      ownKeys.forEach(function (key) {
58
        _defineProperty(target, key, source[key]);
0 ignored issues
show
Bug introduced by
The variable source is changed as part of the for loop for example by arguments.i != null ? arguments.i: {} on line 48. Only the value of the last iteration will be visible in this function if it is called after the loop.
Loading history...
59
      });
60
    }
61
62
    return target;
63
  }
64
65
  /**
66
   * ------------------------------------------------------------------------
67
   * Constants
68
   * ------------------------------------------------------------------------
69
   */
70
71
  var NAME = 'scrollspy';
72
  var VERSION = '4.3.1';
73
  var DATA_KEY = 'bs.scrollspy';
74
  var EVENT_KEY = "." + DATA_KEY;
75
  var DATA_API_KEY = '.data-api';
76
  var JQUERY_NO_CONFLICT = $.fn[NAME];
77
  var Default = {
78
    offset: 10,
79
    method: 'auto',
80
    target: ''
81
  };
82
  var DefaultType = {
83
    offset: 'number',
84
    method: 'string',
85
    target: '(string|element)'
86
  };
87
  var Event = {
88
    ACTIVATE: "activate" + EVENT_KEY,
89
    SCROLL: "scroll" + EVENT_KEY,
90
    LOAD_DATA_API: "load" + EVENT_KEY + DATA_API_KEY
91
  };
92
  var ClassName = {
93
    DROPDOWN_ITEM: 'dropdown-item',
94
    DROPDOWN_MENU: 'dropdown-menu',
95
    ACTIVE: 'active'
96
  };
97
  var Selector = {
98
    DATA_SPY: '[data-spy="scroll"]',
99
    ACTIVE: '.active',
100
    NAV_LIST_GROUP: '.nav, .list-group',
101
    NAV_LINKS: '.nav-link',
102
    NAV_ITEMS: '.nav-item',
103
    LIST_ITEMS: '.list-group-item',
104
    DROPDOWN: '.dropdown',
105
    DROPDOWN_ITEMS: '.dropdown-item',
106
    DROPDOWN_TOGGLE: '.dropdown-toggle'
107
  };
108
  var OffsetMethod = {
109
    OFFSET: 'offset',
110
    POSITION: 'position'
111
    /**
112
     * ------------------------------------------------------------------------
113
     * Class Definition
114
     * ------------------------------------------------------------------------
115
     */
116
117
  };
118
119
  var ScrollSpy =
120
  /*#__PURE__*/
121
  function () {
122
    function ScrollSpy(element, config) {
123
      var _this = this;
124
125
      this._element = element;
126
      this._scrollElement = element.tagName === 'BODY' ? window : element;
127
      this._config = this._getConfig(config);
128
      this._selector = this._config.target + " " + Selector.NAV_LINKS + "," + (this._config.target + " " + Selector.LIST_ITEMS + ",") + (this._config.target + " " + Selector.DROPDOWN_ITEMS);
129
      this._offsets = [];
130
      this._targets = [];
131
      this._activeTarget = null;
132
      this._scrollHeight = 0;
133
      $(this._scrollElement).on(Event.SCROLL, function (event) {
134
        return _this._process(event);
135
      });
136
      this.refresh();
137
138
      this._process();
139
    } // Getters
140
141
142
    var _proto = ScrollSpy.prototype;
143
144
    // Public
145
    _proto.refresh = function refresh() {
146
      var _this2 = this;
147
148
      var autoMethod = this._scrollElement === this._scrollElement.window ? OffsetMethod.OFFSET : OffsetMethod.POSITION;
149
      var offsetMethod = this._config.method === 'auto' ? autoMethod : this._config.method;
150
      var offsetBase = offsetMethod === OffsetMethod.POSITION ? this._getScrollTop() : 0;
151
      this._offsets = [];
152
      this._targets = [];
153
      this._scrollHeight = this._getScrollHeight();
154
      var targets = [].slice.call(document.querySelectorAll(this._selector));
155
      targets.map(function (element) {
156
        var target;
157
        var targetSelector = Util.getSelectorFromElement(element);
158
159
        if (targetSelector) {
160
          target = document.querySelector(targetSelector);
161
        }
162
163
        if (target) {
164
          var targetBCR = target.getBoundingClientRect();
165
166
          if (targetBCR.width || targetBCR.height) {
167
            // TODO (fat): remove sketch reliance on jQuery position/offset
168
            return [$(target)[offsetMethod]().top + offsetBase, targetSelector];
169
          }
170
        }
171
172
        return null;
173
      }).filter(function (item) {
174
        return item;
175
      }).sort(function (a, b) {
176
        return a[0] - b[0];
177
      }).forEach(function (item) {
178
        _this2._offsets.push(item[0]);
179
180
        _this2._targets.push(item[1]);
181
      });
182
    };
183
184
    _proto.dispose = function dispose() {
185
      $.removeData(this._element, DATA_KEY);
186
      $(this._scrollElement).off(EVENT_KEY);
187
      this._element = null;
188
      this._scrollElement = null;
189
      this._config = null;
190
      this._selector = null;
191
      this._offsets = null;
192
      this._targets = null;
193
      this._activeTarget = null;
194
      this._scrollHeight = null;
195
    } // Private
196
    ;
197
198
    _proto._getConfig = function _getConfig(config) {
199
      config = _objectSpread({}, Default, typeof config === 'object' && config ? config : {});
200
201
      if (typeof config.target !== 'string') {
202
        var id = $(config.target).attr('id');
203
204
        if (!id) {
205
          id = Util.getUID(NAME);
206
          $(config.target).attr('id', id);
207
        }
208
209
        config.target = "#" + id;
210
      }
211
212
      Util.typeCheckConfig(NAME, config, DefaultType);
213
      return config;
214
    };
215
216
    _proto._getScrollTop = function _getScrollTop() {
217
      return this._scrollElement === window ? this._scrollElement.pageYOffset : this._scrollElement.scrollTop;
218
    };
219
220
    _proto._getScrollHeight = function _getScrollHeight() {
221
      return this._scrollElement.scrollHeight || Math.max(document.body.scrollHeight, document.documentElement.scrollHeight);
222
    };
223
224
    _proto._getOffsetHeight = function _getOffsetHeight() {
225
      return this._scrollElement === window ? window.innerHeight : this._scrollElement.getBoundingClientRect().height;
226
    };
227
228
    _proto._process = function _process() {
229
      var scrollTop = this._getScrollTop() + this._config.offset;
230
231
      var scrollHeight = this._getScrollHeight();
232
233
      var maxScroll = this._config.offset + scrollHeight - this._getOffsetHeight();
234
235
      if (this._scrollHeight !== scrollHeight) {
236
        this.refresh();
237
      }
238
239
      if (scrollTop >= maxScroll) {
240
        var target = this._targets[this._targets.length - 1];
241
242
        if (this._activeTarget !== target) {
243
          this._activate(target);
244
        }
245
246
        return;
247
      }
248
249
      if (this._activeTarget && scrollTop < this._offsets[0] && this._offsets[0] > 0) {
250
        this._activeTarget = null;
251
252
        this._clear();
253
254
        return;
255
      }
256
257
      var offsetLength = this._offsets.length;
258
259
      for (var i = offsetLength; i--;) {
260
        var isActiveTarget = this._activeTarget !== this._targets[i] && scrollTop >= this._offsets[i] && (typeof this._offsets[i + 1] === 'undefined' || scrollTop < this._offsets[i + 1]);
261
262
        if (isActiveTarget) {
263
          this._activate(this._targets[i]);
264
        }
265
      }
266
    };
267
268
    _proto._activate = function _activate(target) {
269
      this._activeTarget = target;
270
271
      this._clear();
272
273
      var queries = this._selector.split(',').map(function (selector) {
274
        return selector + "[data-target=\"" + target + "\"]," + selector + "[href=\"" + target + "\"]";
275
      });
276
277
      var $link = $([].slice.call(document.querySelectorAll(queries.join(','))));
278
279
      if ($link.hasClass(ClassName.DROPDOWN_ITEM)) {
280
        $link.closest(Selector.DROPDOWN).find(Selector.DROPDOWN_TOGGLE).addClass(ClassName.ACTIVE);
281
        $link.addClass(ClassName.ACTIVE);
282
      } else {
283
        // Set triggered link as active
284
        $link.addClass(ClassName.ACTIVE); // Set triggered links parents as active
285
        // With both <ul> and <nav> markup a parent is the previous sibling of any nav ancestor
286
287
        $link.parents(Selector.NAV_LIST_GROUP).prev(Selector.NAV_LINKS + ", " + Selector.LIST_ITEMS).addClass(ClassName.ACTIVE); // Handle special case when .nav-link is inside .nav-item
288
289
        $link.parents(Selector.NAV_LIST_GROUP).prev(Selector.NAV_ITEMS).children(Selector.NAV_LINKS).addClass(ClassName.ACTIVE);
290
      }
291
292
      $(this._scrollElement).trigger(Event.ACTIVATE, {
293
        relatedTarget: target
294
      });
295
    };
296
297
    _proto._clear = function _clear() {
298
      [].slice.call(document.querySelectorAll(this._selector)).filter(function (node) {
299
        return node.classList.contains(ClassName.ACTIVE);
300
      }).forEach(function (node) {
301
        return node.classList.remove(ClassName.ACTIVE);
302
      });
303
    } // Static
304
    ;
305
306 View Code Duplication
    ScrollSpy._jQueryInterface = function _jQueryInterface(config) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
307
      return this.each(function () {
308
        var data = $(this).data(DATA_KEY);
309
310
        var _config = typeof config === 'object' && config;
311
312
        if (!data) {
313
          data = new ScrollSpy(this, _config);
314
          $(this).data(DATA_KEY, data);
315
        }
316
317
        if (typeof config === 'string') {
318
          if (typeof data[config] === 'undefined') {
319
            throw new TypeError("No method named \"" + config + "\"");
320
          }
321
322
          data[config]();
323
        }
324
      });
325
    };
326
327
    _createClass(ScrollSpy, null, [{
328
      key: "VERSION",
329
      get: function get() {
330
        return VERSION;
331
      }
332
    }, {
333
      key: "Default",
334
      get: function get() {
335
        return Default;
336
      }
337
    }]);
338
339
    return ScrollSpy;
340
  }();
341
  /**
342
   * ------------------------------------------------------------------------
343
   * Data Api implementation
344
   * ------------------------------------------------------------------------
345
   */
346
347
348
  $(window).on(Event.LOAD_DATA_API, function () {
349
    var scrollSpys = [].slice.call(document.querySelectorAll(Selector.DATA_SPY));
350
    var scrollSpysLength = scrollSpys.length;
351
352
    for (var i = scrollSpysLength; i--;) {
353
      var $spy = $(scrollSpys[i]);
354
355
      ScrollSpy._jQueryInterface.call($spy, $spy.data());
356
    }
357
  });
358
  /**
359
   * ------------------------------------------------------------------------
360
   * jQuery
361
   * ------------------------------------------------------------------------
362
   */
363
364
  $.fn[NAME] = ScrollSpy._jQueryInterface;
365
  $.fn[NAME].Constructor = ScrollSpy;
366
367
  $.fn[NAME].noConflict = function () {
368
    $.fn[NAME] = JQUERY_NO_CONFLICT;
369
    return ScrollSpy._jQueryInterface;
370
  };
371
372
  return ScrollSpy;
373
374
}));
375
//# sourceMappingURL=scrollspy.js.map
376